<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>USB2CAN上位机（手机版）</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 引入Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- 引入Font Awesome -->
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    
    <!-- 配置Tailwind自定义颜色和字体 -->
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#007BFF',
                        secondary: '#6c757d',
                        success: '#28a745',
                        danger: '#dc3545',
                        warning: '#ffc107',
                        info: '#17a2b8',
                        light: '#f8f9fa',
                        dark: '#343a40',
                    },
                    fontFamily: {
                        sans: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    
    <!-- 自定义工具类 -->
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .transition-height {
                transition: max-height 0.3s ease-in-out;
            }
            .card-shadow {
                box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
            }
            .table-hover-row:hover {
                background-color: rgba(0, 123, 255, 0.05);
            }
        }
    </style>
</head>

<body class="bg-gray-50 font-sans text-gray-800 min-h-screen">
    <!-- 顶部导航栏 -->
    <header class="bg-white shadow-sm">
        <div class="container mx-auto px-4 py-3 flex justify-between items-center">
            <h1 class="text-xl font-bold text-primary flex items-center">
                <i class="fa fa-microchip mr-2"></i>
                USB2CAN上位机
                <span class="text-xs ml-2 text-gray-500">手机版</span>
            </h1>
            <div class="flex items-center">
                <span class="text-sm text-gray-500 mr-2">仅Edge浏览器可用</span>
                <i class="fa fa-edge text-primary"></i>
            </div>
        </div>
    </header>

    <main class="container mx-auto px-4 py-6">
        <!-- 连接区域 -->
        <div class="bg-white rounded-lg shadow-md p-4 mb-6">
            <button id="connectBtn" class="bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-md transition-all duration-300 flex items-center">
                <i class="fa fa-usb mr-2"></i>
                连接USB设备
            </button>
        </div>

        <!-- 使用说明 -->
        <div class="bg-blue-50 border-l-4 border-blue-400 p-4 mb-6 rounded-r-md">
            <div class="flex">
                <div class="flex-shrink-0">
                    <i class="fa fa-info-circle text-blue-500 text-xl"></i>
                </div>
                <div class="ml-3">
                    <h3 class="text-sm font-medium text-blue-800">手机版网页工具操作须知</h3>
                    <div class="mt-2 text-sm text-blue-700 space-y-2">
                        <ul class="list-disc pl-5 space-y-1">
                            <li>在应用商店下载： Edge浏览器，USB调试助手</li>
                            <li>安卓设备要求：必须运行 Android 10+（部分功能可能需要 Android 11+），
                                必须开启 USB 调试模式（设置 → 关于手机 → 软件信息 → 多次点击版本号开启开发者选项），
                                需要使用支持数据传输的 USB Type-C 数据线，
                                支持 USB OTG（大多数现代安卓设备都支持）</li>
                            <li>每次重新用数据线连接手机和模块之后，都需要先用usb调试助手打开一遍模块:首先点一次open到interface界面，再点第2个interface的open即可，
                                打开一遍之后可关闭usb调试助手，
                                之后在不断连的情况下便可以一直使用Web调用USB2CAN</li>
                        </ul>
                    </div>
                </div>
            </div>
        </div>

        <!-- 发送区域 -->
        <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
            <!-- 通道1发送区域 -->
            <div class="bg-white rounded-lg shadow-md p-5 transform transition-all duration-300 hover:shadow-lg">
                <h4 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
                    <span class="bg-primary/10 text-primary px-2 py-1 rounded text-sm mr-2">通道1</span>
                    CAN通道1发送
                </h4>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
                    <div>
                        <label class="block text-sm font-medium text-gray-700 mb-1">帧类型</label>
                        <select id="channel1DataType" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                            <option value="0">标准帧</option>
                            <option value="1">扩展帧</option>
                        </select>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 mb-1">数据长度</label>
                        <select id="channel1Datalength" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                            <option value="8">数据长度：8</option>
                            <option value="1">数据长度：1</option>
                            <option value="2">数据长度：2</option>
                            <option value="3">数据长度：3</option>
                            <option value="4">数据长度：4</option>
                            <option value="5">数据长度：5</option>
                            <option value="6">数据长度：6</option>
                            <option value="7">数据长度：7</option>
                        </select>
                    </div>
                </div>
                <div class="mb-4">
                    <label class="block text-sm font-medium text-gray-700 mb-1">CAN帧_ID</label>
                    <input type="text" id="channel1CAN_id" placeholder="输入CAN ID" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                </div>
                <div class="mb-4">
                    <label class="block text-sm font-medium text-gray-700 mb-1">发送的CAN数据（仅支持16进制，按回车跳到下一个框）：</label>
                    <div class="grid grid-cols-8 gap-2">
                        <input type="text" id="channel1Data0" placeholder="字节0" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel1Data1" placeholder="字节1" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel1Data2" placeholder="字节2" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel1Data3" placeholder="字节3" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel1Data4" placeholder="字节4" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel1Data5" placeholder="字节5" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel1Data6" placeholder="字节6" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel1Data7" placeholder="字节7" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                    </div>
                </div>
                <div class="flex flex-wrap items-center gap-3 mb-4">
                    <label for="channel1Interval" class="text-sm font-medium text-gray-700">发送间隔(ms):</label>
                    <input type="number" id="channel1Interval" min="0.125" step="0.125" value="1000" placeholder="间隔时间" class="w-24 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                    <button id="channel1StartTimer" class="bg-success hover:bg-success/90 text-white px-4 py-2 rounded-md transition-all duration-300 flex items-center">
                        <i class="fa fa-play mr-1"></i>
                        开始定时
                    </button>
                    <button id="channel1StopTimer" disabled class="bg-gray-300 cursor-not-allowed px-4 py-2 rounded-md transition-all duration-300 flex items-center">
                        <i class="fa fa-stop mr-1"></i>
                        停止定时
                    </button>
                    <span class="timer-status status-inactive text-red-500 font-medium" id="channel1Status">
                        <i class="fa fa-circle-o mr-1"></i>未启动
                    </span>
                </div>
                <button id="channel1SendBtn" class="w-full bg-primary hover:bg-primary/90 text-white px-4 py-3 rounded-md transition-all duration-300 flex items-center justify-center text-lg">
                    <i class="fa fa-paper-plane mr-2"></i>
                    发送数据
                </button>
            </div>

            <!-- 通道2发送区域 -->
            <div class="bg-white rounded-lg shadow-md p-5 transform transition-all duration-300 hover:shadow-lg">
                <h4 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
                    <span class="bg-secondary/10 text-secondary px-2 py-1 rounded text-sm mr-2">通道2</span>
                    CAN通道2发送
                </h4>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
                    <div>
                        <label class="block text-sm font-medium text-gray-700 mb-1">帧类型</label>
                        <select id="channel2DataType" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                            <option value="0">标准帧</option>
                            <option value="1">扩展帧</option>
                        </select>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 mb-1">数据长度</label>
                        <select id="channel2Datalength" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                            <option value="8">数据长度：8</option>
                            <option value="1">数据长度：1</option>
                            <option value="2">数据长度：2</option>
                            <option value="3">数据长度：3</option>
                            <option value="4">数据长度：4</option>
                            <option value="5">数据长度：5</option>
                            <option value="6">数据长度：6</option>
                            <option value="7">数据长度：7</option>
                        </select>
                    </div>
                </div>
                <div class="mb-4">
                    <label class="block text-sm font-medium text-gray-700 mb-1">CAN帧_ID</label>
                    <input type="text" id="channel2CAN_id" placeholder="输入CAN ID" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                </div>
                <div class="mb-4">
                    <label class="block text-sm font-medium text-gray-700 mb-1">发送的CAN数据（仅支持16进制，按回车跳到下一个框）：</label>
                    <div class="grid grid-cols-8 gap-2">
                        <input type="text" id="channel2Data0" placeholder="字节0" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel2Data1" placeholder="字节1" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel2Data2" placeholder="字节2" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel2Data3" placeholder="字节3" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel2Data4" placeholder="字节4" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel2Data5" placeholder="字节5" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel2Data6" placeholder="字节6" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                        <input type="text" id="channel2Data7" placeholder="字节7" class="text-center px-2 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                    </div>
                </div>
                <div class="flex flex-wrap items-center gap-3 mb-4">
                    <label for="channel2Interval" class="text-sm font-medium text-gray-700">发送间隔(ms):</label>
                    <input type="number" id="channel2Interval" min="0.125" step="0.125" value="1000" placeholder="间隔时间" class="w-24 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all">
                    <button id="channel2StartTimer" class="bg-success hover:bg-success/90 text-white px-4 py-2 rounded-md transition-all duration-300 flex items-center">
                        <i class="fa fa-play mr-1"></i>
                        开始定时
                    </button>
                    <button id="channel2StopTimer" disabled class="bg-gray-300 cursor-not-allowed px-4 py-2 rounded-md transition-all duration-300 flex items-center">
                        <i class="fa fa-stop mr-1"></i>
                        停止定时
                    </button>
                    <span class="timer-status status-inactive text-red-500 font-medium" id="channel2Status">
                        <i class="fa fa-circle-o mr-1"></i>未启动
                    </span>
                </div>
                <button id="channel2SendBtn" class="w-full bg-primary hover:bg-primary/90 text-white px-4 py-3 rounded-md transition-all duration-300 flex items-center justify-center text-lg">
                    <i class="fa fa-paper-plane mr-2"></i>
                    发送数据
                </button>
            </div>
        </div>

        <!-- 接收区域 -->
        <div class="bg-white rounded-lg shadow-md p-5 mb-6">
            <h4 class="text-lg font-semibold text-gray-800 mb-4">数据接收区</h4>
            <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-1">接收到的数据：</label>
                    <div id="receiveArea" class="bg-gray-50 border border-gray-200 rounded-md p-3 h-48 overflow-y-auto text-sm font-mono"></div>
                </div>
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-1">系统日志：</label>
                    <div id="logArea" class="bg-gray-50 border border-gray-200 rounded-md p-3 h-48 overflow-y-auto text-sm font-mono"></div>
                </div>
            </div>
        </div>

        <!-- 协议说明折叠面板 -->
        <div class="mb-6">
            <div id="protocolToggle" class="bg-white rounded-lg shadow-md overflow-hidden transition-all duration-300 cursor-pointer hover:shadow-lg">
                <div class="p-4 bg-gradient-to-r from-primary to-blue-700 text-white flex justify-between items-center">
                    <h3 class="text-lg font-semibold flex items-center">
                        <i class="fa fa-book mr-2"></i>
                        USB2CAN模块协议说明
                    </h3>
                    <i id="protocolIcon" class="fa fa-chevron-down transition-transform duration-300"></i>
                </div>
                <div id="protocolContent" class="max-h-0 overflow-hidden transition-all duration-500">
                    <div class="p-6">
                        <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
                            <div class="bg-blue-50 rounded-lg p-4 border-l-4 border-blue-500">
                                <h4 class="text-lg font-semibold text-blue-800 mb-3 flex items-center">
                                    <i class="fa fa-info-circle mr-2"></i>
                                    基本协议格式
                                </h4>
                                <div class="overflow-x-auto">
                                    <table class="min-w-full bg-white rounded-lg overflow-hidden">
                                        <thead class="bg-blue-100 text-blue-800">
                                            <tr>
                                                <th class="py-2 px-4 border-b border-blue-200 text-left">ID（1Byte）</th>
                                                <th class="py-2 px-4 border-b border-blue-200 text-left">数据（nBytes）</th>
                                                <th class="py-2 px-4 border-b border-blue-200 text-left">CRC8校验（1Byte）</th>
                                            </tr>
                                        </thead>
                                        <tbody class="text-blue-700">
                                            <tr class="hover:bg-blue-50 transition-colors">
                                                <td class="py-2 px-4 border-b border-blue-200">决定数据功能</td>
                                                <td class="py-2 px-4 border-b border-blue-200">不定长，由ID决定</td>
                                                <td class="py-2 px-4 border-b border-blue-200">校验</td>
                                            </tr>
                                        </tbody>
                                    </table>
                                </div>
                            </div>
                            
                            <div class="bg-gray-50 rounded-lg p-4 border-l-4 border-gray-500">
                                <h4 class="text-lg font-semibold text-gray-800 mb-3 flex items-center">
                                    <i class="fa fa-exchange mr-2"></i>
                                    数据流向
                                </h4>
                                <div class="space-y-3">
                                    <div class="flex items-center">
                                        <div class="bg-green-100 text-green-800 px-3 py-1 rounded-full text-sm font-medium mr-2">上位机</div>
                                        <i class="fa fa-long-arrow-right text-gray-400 mr-2"></i>
                                        <div class="bg-blue-100 text-blue-800 px-3 py-1 rounded-full text-sm font-medium">模块</div>
                                        <div class="ml-2 text-sm text-gray-600">ID：0XA8</div>
                                    </div>
                                    <div class="flex items-center">
                                        <div class="bg-blue-100 text-blue-800 px-3 py-1 rounded-full text-sm font-medium mr-2">模块</div>
                                        <i class="fa fa-long-arrow-right text-gray-400 mr-2"></i>
                                        <div class="bg-green-100 text-green-800 px-3 py-1 rounded-full text-sm font-medium">上位机</div>
                                        <div class="ml-2 text-sm text-gray-600">ID：0XA9</div>
                                    </div>
                                </div>
                            </div>
                        </div>
                        
                        <div class="bg-white rounded-lg shadow-md p-5 mb-6">
                            <h4 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
                                <i class="fa fa-paper-plane text-primary mr-2"></i>
                                CAN数据发送（上位机 -> 模块）
                            </h4>
                            <div class="bg-primary/5 rounded-lg p-4 mb-4">
                                <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                                    <div>
                                        <p class="text-gray-700 mb-2"><span class="font-semibold">ID：</span>0XA8</p>
                                        <p class="text-gray-700 mb-2"><span class="font-semibold">数据长度：</span>15Bytes</p>
                                    </div>
                                    <div>
                                        <p class="text-gray-700 mb-2"><span class="font-semibold">示例：</span>使用CAN通道：1，can设备ID为：0x7ff，标准帧，数据长度为：4，数据内容为：0xff 0xff 0x00 0x82</p>
                                        <p class="text-gray-700"><span class="font-semibold">发送的数据（16进制）为：</span>A8 01 FF 07 00 00 00 04 FF FF 00 82 E1  （最后一个字节为crc校验结果）</p>
                                    </div>
                                </div>
                            </div>
                            <div class="overflow-x-auto">
                                <table class="min-w-full bg-white rounded-lg overflow-hidden">
                                    <thead class="bg-gray-100 text-gray-800">
                                        <tr>
                                            <th class="py-3 px-4 border-b border-gray-200 text-left">通道数（1Byte）</th>
                                            <th class="py-3 px-4 border-b border-gray-200 text-left">CAN设备ID（4Bytes）</th>
                                            <th class="py-3 px-4 border-b border-gray-200 text-left">帧类型（1Byte）</th>
                                            <th class="py-3 px-4 border-b border-gray-200 text-left">数据长度（1Byte）</th>
                                            <th class="py-3 px-4 border-b border-gray-200 text-left">数据（8Bytes）</th>
                                        </tr>
                                    </thead>
                                    <tbody class="text-gray-700">
                                        <tr class="table-hover-row">
                                            <td class="py-3 px-4 border-b border-gray-200">选择发送数据的CAN设备（1或2）</td>
                                            <td class="py-3 px-4 border-b border-gray-200">CAN数据帧目标设备ID，电机ID，高字节在右</td>
                                            <td class="py-3 px-4 border-b border-gray-200">选择标准帧（0）或拓展帧（1）</td>
                                            <td class="py-3 px-4 border-b border-gray-200">CAN数据帧数据长度（最大为8）</td>
                                            <td class="py-3 px-4 border-b border-gray-200">要发送的数据</td>
                                        </tr>
                                    </tbody>
                                </table>
                            </div>
                        </div>
                        
                        <div class="bg-white rounded-lg shadow-md p-5">
                            <h4 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
                                <i class="fa fa-download text-success mr-2"></i>
                                CAN数据接收（模块 -> 上位机）
                            </h4>
                            <div class="bg-success/5 rounded-lg p-4 mb-4">
                                <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                                    <div>
                                        <p class="text-gray-700 mb-2"><span class="font-semibold">ID：</span>0XA9</p>
                                        <p class="text-gray-700 mb-2"><span class="font-semibold">数据长度：</span>15Bytes</p>
                                    </div>
                                </div>
                            </div>
                            <div class="overflow-x-auto">
                                <table class="min-w-full bg-white rounded-lg overflow-hidden">
                                    <thead class="bg-gray-100 text-gray-800">
                                        <tr>
                                            <th class="py-3 px-4 border-b border-gray-200 text-left">通道数（1Byte）</th>
                                            <th class="py-3 px-4 border-b border-gray-200 text-left">CAN设备ID（4Bytes）</th>
                                            <th class="py-3 px-4 border-b border-gray-200 text-left">帧类型（1Byte）</th>
                                            <th class="py-3 px-4 border-b border-gray-200 text-left">数据长度（1Byte）</th>
                                            <th class="py-3 px-4 border-b border-gray-200 text-left">数据（8Bytes）</th>
                                        </tr>
                                    </thead>
                                    <tbody class="text-gray-700">
                                        <tr class="table-hover-row">
                                            <td class="py-3 px-4 border-b border-gray-200">接收到数据的CAN设备（1或2）</td>
                                            <td class="py-3 px-4 border-b border-gray-200">CAN数据帧ID,电机ID，高字节在右</td>
                                            <td class="py-3 px-4 border-b border-gray-200">标准帧（0）或拓展帧（1）</td>
                                            <td class="py-3 px-4 border-b border-gray-200">收到的CAN数据帧数据长度</td>
                                            <td class="py-3 px-4 border-b border-gray-200">接收到的数据</td>
                                        </tr>
                                    </tbody>
                                </table>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 相关链接 -->
        <div class="bg-white rounded-lg shadow-md p-5">
            <h4 class="text-lg font-semibold text-gray-800 mb-4">相关链接</h4>
            <div class="space-y-3">
                <a href="https://e.tb.cn/h.TBC18sl6EZKXUjL?tk=C5g5eLgyMf6HU071" class="text-primary hover:text-primary/80 transition-colors flex items-center">
                    <i class="fa fa-shopping-cart mr-2"></i>
                    USB转2路CAN模块购买地址
                </a>
                <a href="https://pan.baidu.com/s/1EwYDNQ0jMKyTSvJEEcj6aw?pwd=10ob" class="text-primary hover:text-primary/80 transition-colors flex items-center">
                    <i class="fa fa-file-text-o mr-2"></i>
                    模块说明书以及SDK下载地址
                </a>
            </div>
        </div>
    </main>

    <footer class="bg-gray-800 text-white py-6 mt-8">
        <div class="container mx-auto px-4">
            <div class="flex flex-col md:flex-row justify-between items-center">
                <div class="mb-4 md:mb-0">
                    <h3 class="text-lg font-semibold">USB2CAN上位机</h3>
                    <p class="text-gray-400 text-sm mt-1">用于USB转CAN模块的调试工具</p>
                </div>
                <div class="flex space-x-4">
                    <a href="#" class="text-gray-400 hover:text-white transition-colors">
                        <i class="fa fa-question-circle"></i>
                        <span class="ml-1">帮助</span>
                    </a>
                    <a href="#" class="text-gray-400 hover:text-white transition-colors">
                        <i class="fa fa-envelope"></i>
                        <span class="ml-1">联系我们</span>
                    </a>
                </div>
            </div>
            <div class="border-t border-gray-700 mt-4 pt-4 text-center text-gray-500 text-sm">
                &copy; 2025 USB2CAN上位机版权所有
            </div>
        </div>
    </footer>


    <script>
        const CRC8_INIT = 0xff;
        const CRC8_TAB = [
            0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, 0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41,
            0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e, 0x5f, 0x01, 0xe3, 0xbd, 0x3e, 0x60, 0x82, 0xdc,
            0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0, 0xe1, 0xbf, 0x5d, 0x03, 0x80, 0xde, 0x3c, 0x62,
            0xbe, 0xe0, 0x02, 0x5c, 0xdf, 0x81, 0x63, 0x3d, 0x7c, 0x22, 0xc0, 0x9e, 0x1d, 0x43, 0xa1, 0xff,
            0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5, 0x84, 0xda, 0x38, 0x66, 0xe5, 0xbb, 0x59, 0x07,
            0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x06, 0x58, 0x19, 0x47, 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a,
            0x65, 0x3b, 0xd9, 0x87, 0x04, 0x5a, 0xb8, 0xe6, 0xa7, 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24,
            0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b, 0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x05, 0xe7, 0xb9,
            0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 0x0f, 0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd,
            0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 0xcc, 0x92, 0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0x0e, 0x50,
            0xaf, 0xf1, 0x13, 0x4d, 0xce, 0x90, 0x72, 0x2c, 0x6d, 0x33, 0xd1, 0x8f, 0x0c, 0x52, 0xb0, 0xee,
            0x32, 0x6c, 0x8e, 0xd0, 0x53, 0x0d, 0xef, 0xb1, 0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73,
            0xca, 0x94, 0x76, 0x28, 0xab, 0xf5, 0x17, 0x49, 0x08, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b,
            0x57, 0x09, 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4, 0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16,
            0xe9, 0xb7, 0x55, 0x0b, 0x88, 0xd6, 0x34, 0x6a, 0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8,
            0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7, 0xb6, 0xe8, 0x0a, 0x54, 0xd7, 0x89, 0x6b, 0x35
        ];

        let usbDevice = null;
        let isHexMode = false;
        let channel1Timer = null;
        let channel2Timer = null;
        const connectBtn = document.getElementById('connectBtn');
        const channel1SendBtn = document.getElementById('channel1SendBtn');
        const channel2SendBtn = document.getElementById('channel2SendBtn');
        const channel1StartTimer = document.getElementById('channel1StartTimer');
        const channel1StopTimer = document.getElementById('channel1StopTimer');
        const channel2StartTimer = document.getElementById('channel2StartTimer');
        const channel2StopTimer = document.getElementById('channel2StopTimer');
        const channel1Interval = document.getElementById('channel1Interval');
        const channel2Interval = document.getElementById('channel2Interval');
        const channel1Status = document.getElementById('channel1Status');
        const channel2Status = document.getElementById('channel2Status');
        const channel1DataType = document.getElementById('channel1DataType');
        const channel1Datalength = document.getElementById('channel1Datalength');
        const channel1CAN_id = document.getElementById('channel1CAN_id');
        const channel1DataInputs = [
            document.getElementById('channel1Data0'),
            document.getElementById('channel1Data1'),
            document.getElementById('channel1Data2'),
            document.getElementById('channel1Data3'),
            document.getElementById('channel1Data4'),
            document.getElementById('channel1Data5'),
            document.getElementById('channel1Data6'),
            document.getElementById('channel1Data7')
        ];
        const channel2DataType = document.getElementById('channel2DataType');
        const channel2Datalength = document.getElementById('channel2Datalength');
        const channel2CAN_id = document.getElementById('channel2CAN_id');
        const channel2DataInputs = [
            document.getElementById('channel2Data0'),
            document.getElementById('channel2Data1'),
            document.getElementById('channel2Data2'),
            document.getElementById('channel2Data3'),
            document.getElementById('channel2Data4'),
            document.getElementById('channel2Data5'),
            document.getElementById('channel2Data6'),
            document.getElementById('channel2Data7')
        ];
        const logArea = document.getElementById('logArea');
        const receiveArea = document.getElementById('receiveArea');
        const protocolToggle = document.getElementById('protocolToggle');
        const protocolContent = document.getElementById('protocolContent');

        // 回车跳到下一个数据栏（通道1）
        channel1DataInputs.forEach((input, index) => {
            input.addEventListener('keydown', (event) => {
                if (event.key === 'Enter') {
                    event.preventDefault();
                    if (index < channel1DataInputs.length - 1) {
                        channel1DataInputs[index + 1].focus();
                    } else {
                        channel1SendBtn.click();
                    }
                }
            });
        });

        // 回车跳到下一个数据栏（通道2）
        channel2DataInputs.forEach((input, index) => {
            input.addEventListener('keydown', (event) => {
                if (event.key === 'Enter') {
                    event.preventDefault();
                    if (index < channel2DataInputs.length - 1) {
                        channel2DataInputs[index + 1].focus();
                    } else {
                        channel2SendBtn.click();
                    }
                }
            });
        });

        // 连接USB设备
        connectBtn.addEventListener('click', async () => {
            try {
                if (usbDevice && usbDevice.opened) {
                    log('已连接到USB设备，无需重复连接');
                    return;
                }

                // 过滤特定的USB设备（根据实际设备的vendorId和productId调整）
                const filters = [ ];

                // 请求选择USB设备
                usbDevice = await navigator.usb.requestDevice({ filters });
                if (!usbDevice) {
                    log('未选择任何USB设备');
                    return;
                }

                log(`已选择设备: ${usbDevice.productName || '未知设备'}`);
                
                // 打开设备
                await usbDevice.open();
                
                // 选择配置
                if (usbDevice.configuration === null) {
                    await usbDevice.selectConfiguration(1);
                }
                
                // 声明接口
                await usbDevice.claimInterface(1);
                
                
                log('成功连接到USB设备');
                log(`使用 IN 端点: 1`);
                log(`使用 OUT 端点:1`);
                
                // 开始接收数据
                startReceivingData();
            } catch (error) {
                log(`连接失败: ${error.message}`);
            }
        });

        // 数据接收函数
        async function startReceivingData() {
            if (!usbDevice || !usbDevice.opened) return;
            
            // 获取IN端点
            const interfaces = 1;
            const inEndpoint = 1;
            
  
            
            log('开始接收数据...');
            
            // 循环接收数据
            while (usbDevice.opened) {
                try {
                    const result = await usbDevice.transferIn(1, 64);
                    const buffer = result.data;
                    
                    if (buffer) {
                        // 处理接收到的数据
                        processReceivedData(new Uint8Array(buffer.buffer));
                    }
                } catch (error) {
                    if (error.message !== 'Device is closed') {
                        log(`接收数据时出错: ${error.message}`);
                    }
                    break;
                }
            }
            
            log('数据接收已停止');
        }

        // 数据处理函数
        function processReceivedData(buffer) {
            const value = new Uint8Array(buffer);

            // 简单的帧验证 - 假设CAN帧至少有一定长度
            if (value.length < 6) {
                log(`接收到不完整的帧: ${bytesToHexString(value)}`);
                return;
            }

            try {
                // 转换为16进制显示
                const hexStr = bytesToHexString(value);
                log(`接收16进制: ${hexStr}`);

                // 解码并显示在接收框
                if (value[0] === 0xA9) {
                    const channelNum = value[1];
                    const canId = (value[5] << 24) | (value[4] << 16) | (value[3] << 8) | value[2];
                    const frameType = value[6];
                    const dataLength = value[7];
                    const data = value.slice(8, 8 + dataLength);

                    const decodedMessage = `通道: ${channelNum}   , CANID: 0x${canId.toString(16).padStart(8, '0')},  ${frameType === 0 ? '标准帧' : '扩展帧'}, 长度: ${dataLength}, 内容: ${bytesToHexString(data)}`;
                    displayReceivedData(decodedMessage);
                }
            } catch (error) {
                log(`解析数据时出错: ${error.message}`);
                log(`原始数据: ${bytesToHexString(value)}`);
            }
        }

        // 通道1发送数据
        channel1SendBtn.addEventListener('click', async () => {
            if (!usbDevice || !usbDevice.opened) {
                log('请先连接USB设备');
                return;
            }

            try {
                const canIdValue = parseInt(channel1CAN_id.value, 16);
                const dataBuffer = new Uint8Array([
                    0xA8, 1,
                    canIdValue & 0xff,           // 第一个字节
                    (canIdValue >> 8) & 0xff,    // 第二个字节
                    (canIdValue >> 16) & 0xff,   // 第三个字节
                    (canIdValue >> 24) & 0xff,   // 第四个字节
                    parseInt(channel1DataType.value), parseInt(channel1Datalength.value),
                    ...channel1DataInputs.map(input => parseInt(input.value, 16) || 0)
                ]);

                const crcDataBuffer = appendCRC8CheckSum(dataBuffer);
                
                // 获取OUT端点
                const interfaces =1;
                const outEndpoint = 1;

                
                // 发送数据
                await usbDevice.transferOut(1, crcDataBuffer);
                log(`通道1 发送16进制: ${bytesToHexString(crcDataBuffer)}`);
            } catch (error) {
                log(`通道1 发送失败: ${error.message}`);
            }
        });

        // 通道2发送数据
        channel2SendBtn.addEventListener('click', async () => {
            if (!usbDevice || !usbDevice.opened) {
                log('请先连接USB设备');
                return;
            }

            try {
                const canIdValue = parseInt(channel2CAN_id.value, 16);
                const dataBuffer = new Uint8Array([
                    0xA8, 2,
                    canIdValue & 0xff,           // 第一个字节
                    (canIdValue >> 8) & 0xff,    // 第二个字节
                    (canIdValue >> 16) & 0xff,   // 第三个字节
                    (canIdValue >> 24) & 0xff,   // 第四个字节
                    parseInt(channel2DataType.value), parseInt(channel2Datalength.value),
                    ...channel2DataInputs.map(input => parseInt(input.value, 16) || 0)
                ]);

                const crcDataBuffer = appendCRC8CheckSum(dataBuffer);
                
                // 获取OUT端点
                const interfaces =1;
                const outEndpoint = 1;
                
        
                
                // 发送数据
                await usbDevice.transferOut(1, crcDataBuffer);
                log(`通道2 发送16进制: ${bytesToHexString(crcDataBuffer)}`);
            } catch (error) {
                log(`通道2 发送失败: ${error.message}`);
            }
        });

        // 通道1定时发送
        channel1StartTimer.addEventListener('click', () => {
            if (!usbDevice || !usbDevice.opened) {
                log('请先连接USB设备');
                return;
            }

            const interval = parseFloat(channel1Interval.value);
            if (isNaN(interval) || interval <= 0) {
                log('请输入有效的间隔时间');
                return;
            }

            // 限制最大频率为8000Hz (最小间隔为0.125ms)
            const safeInterval = Math.max(interval, 0.125);
            if (safeInterval !== interval) {
                channel1Interval.value = safeInterval;
                log('已将间隔时间调整为最小值0.125ms (对应8000Hz)');
            }

            channel1Timer = setInterval(() => {
                channel1SendBtn.click();
            }, safeInterval);

            channel1StartTimer.disabled = true;
            channel1StopTimer.disabled = false;
            channel1Status.textContent = '已启动';
            channel1Status.className = 'timer-status status-active';
            log(`通道1定时发送已启动，间隔 ${safeInterval}ms`);
        });

        // 通道1停止定时发送
        channel1StopTimer.addEventListener('click', () => {
            if (channel1Timer) {
                clearInterval(channel1Timer);
                channel1Timer = null;
            }

            channel1StartTimer.disabled = false;
            channel1StopTimer.disabled = true;
            channel1Status.textContent = '未启动';
            channel1Status.className = 'timer-status status-inactive';
            log('通道1定时发送已停止');
        });

        // 通道2定时发送
        channel2StartTimer.addEventListener('click', () => {
            if (!usbDevice || !usbDevice.opened) {
                log('请先连接USB设备');
                return;
            }

            const interval = parseFloat(channel2Interval.value);
            if (isNaN(interval) || interval <= 0) {
                log('请输入有效的间隔时间');
                return;
            }

            // 限制最大频率为8000Hz (最小间隔为0.125ms)
            const safeInterval = Math.max(interval, 0.125);
            if (safeInterval !== interval) {
                channel2Interval.value = safeInterval;
                log('已将间隔时间调整为最小值0.125ms (对应8000Hz)');
            }

            channel2Timer = setInterval(() => {
                channel2SendBtn.click();
            }, safeInterval);

            channel2StartTimer.disabled = true;
            channel2StopTimer.disabled = false;
            channel2Status.textContent = '已启动';
            channel2Status.className = 'timer-status status-active';
            log(`通道2定时发送已启动，间隔 ${safeInterval}ms`);
        });

        // 通道2停止定时发送
        channel2StopTimer.addEventListener('click', () => {
            if (channel2Timer) {
                clearInterval(channel2Timer);
                channel2Timer = null;
            }

            channel2StartTimer.disabled = false;
            channel2StopTimer.disabled = true;
            channel2Status.textContent = '未启动';
            channel2Status.className = 'timer-status status-inactive';
            log('通道2定时发送已停止');
        });

        // 页面卸载时清理定时器和关闭USB设备
        window.addEventListener('beforeunload', async () => {
            if (channel1Timer) clearInterval(channel1Timer);
            if (channel2Timer) clearInterval(channel2Timer);
            
            if (usbDevice && usbDevice.opened) {
                try {
                    await usbDevice.close();
                    log('已关闭USB设备连接');
                } catch (error) {
                    log(`关闭USB设备时出错: ${error.message}`);
                }
            }
        });

        // 日志输出
        function log(message) {
            const now = new Date().toLocaleTimeString();
            logArea.innerHTML += `<div>[${now}] ${message}</div>`;
            logArea.scrollTop = logArea.scrollHeight;
        }

        // 显示接收数据
        function displayReceivedData(message) {
            const now = new Date().toLocaleTimeString();
            receiveArea.innerHTML += `<div>[${now}] ${message}   </div>`;
            receiveArea.scrollTop = receiveArea.scrollHeight;
        }

        // 辅助函数: 字节数组转16进制字符串
        function bytesToHexString(bytes) {
            return Array.from(bytes)
               .map(b => b.toString(16).padStart(2, '0'))
               .join(' ');
        }

        // CRC8校验相关函数
        function getCRC8CheckSum(data, initialValue = CRC8_INIT) {
            let crc = initialValue;
            for (let i = 0; i < data.length; i++) {
                const index = crc ^ data[i];
                crc = CRC8_TAB[index];
            }
            return crc;
        }

        function verifyCRC8CheckSum(data) {
            if (data.length < 2) return false;
            const dataWithoutCrc = data.slice(0, -1);
            const expectedCrc = getCRC8CheckSum(dataWithoutCrc);
            const actualCrc = data[data.length - 1];
            return expectedCrc === actualCrc;
        }

        function appendCRC8CheckSum(data) {
            const crc = getCRC8CheckSum(data);
            const result = new Uint8Array([...data, crc]);
            return result;
        }

        // 辅助函数: 16进制字符串转字节数组
        function hexStringToBytes(hexString) {
            const bytes = [];
            for (let i = 0; i < hexString.length; i += 2) {
                bytes.push(parseInt(hexString.substr(i, 2), 16));
            }
            return new Uint8Array(bytes);
        }
        // 获取协议说明的切换按钮、内容区域和箭头图标元素
        const protocolIcon = document.getElementById('protocolIcon');

        // 为协议说明的切换按钮添加点击事件监听器
        protocolToggle.addEventListener('click', () => {
            // 检查协议说明内容区域的最大高度
            if (protocolContent.style.maxHeight) {
                // 如果最大高度已设置，说明内容是展开的，将其折叠
                protocolContent.style.maxHeight = null;
                // 旋转箭头图标回到初始状态
                protocolIcon.style.transform = 'rotate(0deg)';
            } else {
                // 如果最大高度未设置，说明内容是折叠的，将其展开
                protocolContent.style.maxHeight = protocolContent.scrollHeight + 'px';
                // 旋转箭头图标 180 度，指示内容已展开
                protocolIcon.style.transform = 'rotate(180deg)';
            }
        });
    </script>
</body>

</html>    